﻿#region Copyright (c) 2006-2012 nHydrate.org, All Rights Reserved
// --------------------------------------------------------------------- *
//                           NHYDRATE.ORG                                *
//              Copyright (c) 2006-2012 All Rights reserved              *
//                                                                       *
//                                                                       *
// This file and its contents are protected by United States and         *
// International copyright laws.  Unauthorized reproduction and/or       *
// distribution of all or any portion of the code contained herein       *
// is strictly prohibited and will result in severe civil and criminal   *
// penalties.  Any violations of this copyright will be prosecuted       *
// to the fullest extent possible under law.                             *
//                                                                       *
// THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
// TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
// TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
// CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
// THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF THE NHYDRATE GROUP *
//                                                                       *
// UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
// PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
// SUBSTANTIALLY THE SAME, FUNCTIONALITY AS THIS PRODUCT                 *
//                                                                       *
// THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
// CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF NHYDRATE,          *
// THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO             *
// INSURE ITS CONFIDENTIALITY.                                           *
//                                                                       *
// THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
// PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
// EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
// THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
// SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                       *
// THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
// --------------------------------------------------------------------- *
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using DslModeling = global::Microsoft.VisualStudio.Modeling;
using DslValidation = global::Microsoft.VisualStudio.Modeling.Validation;
using System.IO;
using System.Xml;
using nHydrate.Generator.Common.Util;
using nHydrate.Dsl.Objects;

namespace nHydrate.Dsl
{
	partial class nHydrateSerializationHelper
	{
		private const string LAST_MODEL_MODEL_COMPATIBLE = "5.0.2.107";

		public override void SaveModelAndDiagram(Microsoft.VisualStudio.Modeling.SerializationResult serializationResult, nHydrateModel modelRoot, string modelFileName, nHydrateDiagram diagram, string diagramFileName, Encoding encoding, bool writeOptionalPropertiesWithDefaultValue)
		{
			var mainInfo = new FileInfo(modelFileName);
			modelRoot.ModelFileName = modelFileName;
			var modelName = mainInfo.Name.Replace(".nhydrate", ".model");

			if (modelRoot.ModelToDisk)
			{
				nHydrate.Dsl.Custom.SQLFileManagement.SaveToDisk(modelRoot, mainInfo.DirectoryName, modelName, diagram);
			}
			else
			{
				try
				{
					var f = nHydrate.Dsl.Custom.SQLFileManagement.GetModelFolder(mainInfo.DirectoryName, modelName);
					if (Directory.Exists(f)) Directory.Delete(f, true);
				}
				catch { }
			}

			base.SaveModelAndDiagram(serializationResult, modelRoot, modelFileName, diagram, diagramFileName, encoding, writeOptionalPropertiesWithDefaultValue);

			//Model File
			if (modelRoot.ModelToDisk)
			{
				var document = new XmlDocument();
				document.Load(modelFileName);

				//Remove entire node for Views, Stored Procedures, and Functions
				for (var ii = document.DocumentElement.ChildNodes.Count - 1; ii >= 0; ii--)
				{
					var n = document.DocumentElement.ChildNodes[ii];
					document.DocumentElement.RemoveChild(n);
				}

				document.Save(modelFileName);
			}

			//Diagram File
			if (modelRoot.ModelToDisk)
			{
				//Now gut the diagram file
				var diagramFile = modelFileName + ".diagram";
				if (File.Exists(diagramFile))
				{
					var document = new XmlDocument();
					document.Load(diagramFile);

					//Remove all child nodes
					var n = document.DocumentElement.SelectSingleNode("nestedChildShapes");
					if (n != null)
					{
						document.DocumentElement.RemoveChild(n);
						document.Save(diagramFile);
					}

					////Reset position attributes
					//var nlist = document.DocumentElement.SelectNodes("//entityShape");
					//foreach (XmlElement node in nlist)
					//{
					//  //XmlHelper.RemoveAttribute(node, "absoluteBounds");
					//  XmlHelper.AddAttribute(node, "absoluteBounds", "0, 0, 0, 0");
					//  var clist = node.SelectNodes("nestedChildShapes/elementListCompartment");
					//  foreach (XmlElement node2 in clist)
					//  {
					//    //XmlHelper.RemoveAttribute(node2, "absoluteBounds");
					//    XmlHelper.AddAttribute(node2, "absoluteBounds", "0, 0, 0, 0");
					//  }
					//}
					//document.Save(diagramFile);
				}
			}

			//Save the refactorizations
			if (modelRoot.Refactorizations.Count > 0)
			{
				var document = new XmlDocument();
				document.Load(modelFileName);
				var refactorList = XmlHelper.AddElement(document.DocumentElement, "refactorizations");
				foreach (var item in modelRoot.Refactorizations)
				{
					var n = XmlHelper.AddElement((XmlElement)refactorList, "refactor");
					item.ToXML((XmlElement)n);
				}
				document.Save(modelFileName);
			}

		}

		public override nHydrateModel LoadModelAndDiagram(DslModeling::SerializationResult serializationResult, DslModeling::Partition modelPartition, string modelFileName, DslModeling::Partition diagramPartition, string diagramFileName, DslModeling::ISchemaResolver schemaResolver, DslValidation::ValidationController validationController, DslModeling::ISerializerLocator serializerLocator)
		{
			var modelRoot = base.LoadModelAndDiagram(serializationResult, modelPartition, modelFileName, diagramPartition, diagramFileName, schemaResolver, validationController, serializerLocator);

			//Verify that we can open the model
			var thisAssem = System.Reflection.Assembly.GetExecutingAssembly();
			var thisAssemName = thisAssem.GetName();
			var toolVersion = thisAssemName.Version;
			var modelVersion = new Version(0, 0);
			if (!string.IsNullOrEmpty(modelRoot.ModelVersion))
				modelVersion = new Version(modelRoot.ModelVersion);
			if (toolVersion < modelVersion)
				throw new Exception("This model was created with newer version of the modeler. Please install version '" + modelVersion.ToString() + "' or higher.");

			//When saved the new version will be this tool version
			modelRoot.ModelVersion = LAST_MODEL_MODEL_COMPATIBLE;
			modelRoot.ModelFileName = modelFileName;

			modelRoot.IsDirty = false;

			var mainInfo = new FileInfo(modelFileName);
			var modelName = mainInfo.Name.Replace(".nhydrate", ".model");

			if (modelRoot.ModelToDisk)
			{
				//Load from disk store
				nHydrate.Dsl.Custom.SQLFileManagement.LoadFromDisk(modelRoot, mainInfo.DirectoryName, modelRoot.Partition.Store, modelName);
			}
			else
			{
				try
				{
					var f = nHydrate.Dsl.Custom.SQLFileManagement.GetModelFolder(mainInfo.DirectoryName, modelName);
					if (Directory.Exists(f)) Directory.Delete(f, true);
				}
				catch { }
			}

			modelRoot.IsDirty = false;

			//SyncServer
			var syncServerFile = modelFileName + ".syncserver";
			modelRoot.SyncServerURL = nHydrate.SyncServer.Client.SyncDomain.LoadSyncServerConfig(syncServerFile);

			//Alphabetized columns if need be
			//foreach (var entity in modelRoot.Entities)
			//{
			//  entity.Fields.Sort((x, y) => x.Name.CompareTo(y.Name));
			//}

			#region Load Indexes
			//For now load the indexes into the REAL indexes collection
			//This should only happens the first time
			using (var transaction = modelRoot.Store.TransactionManager.BeginTransaction(Guid.NewGuid().ToString()))
			{
				LoadInitialIndexes(modelRoot);
				transaction.Commit();
			}
			#endregion

			#region Handle the Precedence
			if (modelRoot.StoredProcedures.Count > 0)
				modelRoot.MaxPrecedenceOrder = Math.Max(modelRoot.StoredProcedures.Max(x => x.PrecedenceOrder), modelRoot.MaxPrecedenceOrder);
			if (modelRoot.Views.Count > 0)
				modelRoot.MaxPrecedenceOrder = Math.Max(modelRoot.Views.Max(x => x.PrecedenceOrder), modelRoot.MaxPrecedenceOrder);
			if (modelRoot.Functions.Count > 0)
				modelRoot.MaxPrecedenceOrder = Math.Max(modelRoot.Functions.Max(x => x.PrecedenceOrder), modelRoot.MaxPrecedenceOrder);

			modelRoot.StoredProcedures.Where(x => x.PrecedenceOrder == 0).ToList().ForEach(x => x.PrecedenceOrder = ++modelRoot.MaxPrecedenceOrder);
			modelRoot.Views.Where(x => x.PrecedenceOrder == 0).ToList().ForEach(x => x.PrecedenceOrder = ++modelRoot.MaxPrecedenceOrder);
			modelRoot.Functions.Where(x => x.PrecedenceOrder == 0).ToList().ForEach(x => x.PrecedenceOrder = ++modelRoot.MaxPrecedenceOrder);
			#endregion

			#region Load the refactorizations
			if (File.Exists(modelFileName))
			{
				var fi = new FileInfo(modelFileName);
				if (fi.Length > 5)
				{
					var document = new XmlDocument();
					document.Load(modelFileName);
					if (document.DocumentElement != null)
					{
						var refactorList = document.DocumentElement.SelectSingleNode("refactorizations");
						if (refactorList != null)
						{
							foreach (XmlNode n in refactorList.ChildNodes)
							{
								//if (XmlHelper.GetAttributeValue(n, "type", string.Empty) == "guidtoid")
								//{
								//  modelRoot.Refactorizations.Add(new RefactorChangeGuidToID((XmlElement)n));
								//}
								//else if (XmlHelper.GetAttributeValue(n, "type", string.Empty) == "guidtoididtoguid")
								//{
								//  modelRoot.Refactorizations.Add(new RefactorChangeIDToGuid((XmlElement)n));
								//}
								if (XmlHelper.GetAttributeValue(n, "type", string.Empty) == "splittable")
								{
									modelRoot.Refactorizations.Add(new RefactorTableSplit((XmlElement)n));
								}
								else if (XmlHelper.GetAttributeValue(n, "type", string.Empty) == "combinetable")
								{
									modelRoot.Refactorizations.Add(new RefactorTableCombine((XmlElement)n));
								}
							}
						}
					}
				}
			}
			#endregion

#if MYSQL
			//Do Nothing
#else
			modelRoot.SupportedPlatforms = DatabasePlatformConstants.SQLServer;
#endif

			return modelRoot;
		}

		public static void LoadInitialIndexes(nHydrateModel modelRoot)
		{
			//Setup primary keys
			foreach (var entity in modelRoot.Entities)
			{
				if (entity.Indexes.Count(x => x.IndexType == IndexTypeConstants.PrimaryKey) == 0)
				{
					var newIndex = new Index(entity.Partition);
					newIndex.ParentEntityID = entity.Id;
					newIndex.IndexType = IndexTypeConstants.PrimaryKey;
					newIndex.Clustered = true;
					entity.Indexes.Add(newIndex);

					foreach (var field in entity.PrimaryKeyFields)
					{
						var newColumn = new IndexColumn(field.Partition);
						newColumn.FieldID = field.Id;
						newColumn.IsInternal = true;
						newIndex.IndexColumns.Add(newColumn);
					}
				}
			}

			var allIndexedField = modelRoot.Entities.SelectMany(x => x.Fields).Where(x => x.IsIndexed && !x.IsPrimaryKey);
			var allIndexes = modelRoot.Entities.SelectMany(x => x.Indexes);
			foreach (var field in allIndexedField)
			{
				var index = allIndexes.FirstOrDefault(x =>
					x.IndexColumns.Count == 1 &&
					x.IndexColumns.First().FieldID == field.Id &&
					x.IndexColumns.First().Ascending);

				if (index == null)
				{
					var newIndex = new Index(modelRoot.Partition);
					newIndex.ParentEntityID = field.Entity.Id;
					newIndex.IndexType = IndexTypeConstants.IsIndexed;
					field.Entity.Indexes.Add(newIndex);

					var newColumn = new IndexColumn(modelRoot.Partition);
					newColumn.FieldID = field.Id;
					newColumn.IsInternal = true;
					newIndex.IndexColumns.Add(newColumn);
				}
			}
		}
	}

	/// <summary>
	/// This is how you overload the serialization process of the Model
	/// </summary>
	partial class nHydrateModelSerializer
	{

		protected override void WriteElements(Microsoft.VisualStudio.Modeling.SerializationContext serializationContext, Microsoft.VisualStudio.Modeling.ModelElement element, System.Xml.XmlWriter writer)
		{
			base.WriteElements(serializationContext, element, writer);

			//  writer.WriteStartElement("custom");
			//  writer.WriteCData("my custom stuff here!");
			//  writer.WriteEndElement();
		}

		//protected override void WritePropertiesAsAttributes(Microsoft.VisualStudio.Modeling.SerializationContext serializationContext, Microsoft.VisualStudio.Modeling.ModelElement element, System.Xml.XmlWriter writer)
		//{
		//  base.WritePropertiesAsAttributes(serializationContext, element, writer);

		//  var instanceOfnHydrateModel = element as nHydrateModel;

		//  // CompanyName
		//  if (!serializationContext.Result.Failed)
		//  {
		//    var propValue = instanceOfnHydrateModel.TestProperty;
		//    if (!serializationContext.Result.Failed)
		//    {
		//      nHydrateSerializationHelper.Instance.WriteAttributeString(serializationContext, element, writer, "testProperty", propValue.ToString().ToLower());
		//    }
		//  }
		//}
	}

}